package com.ruoyi.framework.config;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class OAuth2CacheManager {

    @Value("${token.expireTime}")
    private int expireTime;

    public static String ACCESS_TOKEN_KEY = "_access_token";
    public static String ID_TOKEN_KEY = "_id_token";
    public static String REFRESH_TOKEN_KEY = "_refresh_token";
    public static String ACCESS_TOKEN_EXPIRE_AT_KEY = "_expire_at";

    private final RedisTemplate<String, String> redisTemplate;

    public OAuth2CacheManager(RedisConnectionFactory redisConnectionFactory){
        redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 设置键和值的序列化器为 StringRedisSerializer
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        // 手动调用 afterPropertiesSet() 方法
        try {
            redisTemplate.afterPropertiesSet();
        } catch (Exception e) {
            log.error("Failed to initialize RedisTemplate: {}", e.getMessage(), e);
            throw new RuntimeException("Failed to initialize RedisTemplate", e);
        }
    }

    public void clearCache(HttpServletRequest request, String sid){
        if(StringUtils.isNotBlank(sid)){
            redisTemplate.delete(sid + ACCESS_TOKEN_KEY);
            redisTemplate.delete(sid + ID_TOKEN_KEY);
            redisTemplate.delete(sid + REFRESH_TOKEN_KEY);
            redisTemplate.delete(sid + ACCESS_TOKEN_EXPIRE_AT_KEY);
        }else{
            HttpSession session = request.getSession(false);
            if (session != null) {
                session.removeAttribute(ACCESS_TOKEN_KEY);
                session.removeAttribute(ID_TOKEN_KEY);
                session.removeAttribute(REFRESH_TOKEN_KEY);
                session.removeAttribute(ACCESS_TOKEN_EXPIRE_AT_KEY);
            }
        }
    }

    public void saveAccessToken(HttpServletRequest request, String sid, String accessToken){
        if(StringUtils.isNotBlank(sid)){
            this.set(sid + ACCESS_TOKEN_KEY, accessToken);
            redisTemplate.expire(sid + ACCESS_TOKEN_KEY, expireTime, TimeUnit.MINUTES);
        }else{
            request.getSession().setAttribute(ACCESS_TOKEN_KEY, accessToken);
        }
    }

    public void updateAccessToken(HttpServletRequest request, String sid, String accessToken){
        if(StringUtils.isNotBlank(sid)){
            if(Boolean.FALSE.equals(redisTemplate.hasKey(sid + ACCESS_TOKEN_KEY))){
                saveAccessToken(request, sid, accessToken);
            }else{
                this.set(sid + ACCESS_TOKEN_KEY, accessToken);
            }
        }else{
            request.getSession().setAttribute(ACCESS_TOKEN_KEY, accessToken);
        }
    }

    public String getAccessToken(HttpServletRequest request, String sid){
        if(StringUtils.isNotBlank(sid)){
            if(Boolean.TRUE.equals(redisTemplate.hasKey(sid + ACCESS_TOKEN_KEY))){
                return this.get(sid + ACCESS_TOKEN_KEY);
            }
        }else{
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object obj =  session.getAttribute(ACCESS_TOKEN_KEY);
                if(obj != null) {
                    return (String)obj;
                }
            }
        }
        return null;
    }

    public void saveIdToken(HttpServletRequest request, String sid, String idToken){
        if(StringUtils.isNotBlank(sid)){
            this.set(sid + ID_TOKEN_KEY, idToken);
            redisTemplate.expire(sid + ID_TOKEN_KEY, expireTime, TimeUnit.MINUTES);
        }else{
            request.getSession().setAttribute(ID_TOKEN_KEY, idToken);
        }
    }

    public void updateIdToken(HttpServletRequest request, String sid, String idToken){
        if(StringUtils.isNotBlank(sid)){
            if(Boolean.FALSE.equals(redisTemplate.hasKey(sid + ID_TOKEN_KEY))){
                saveIdToken(request, sid, idToken);
            }else{
                this.set(sid + ID_TOKEN_KEY, idToken);
            }
        }else{
            request.getSession().setAttribute(ID_TOKEN_KEY, idToken);
        }
    }

    public String getIdToken(HttpServletRequest request, String sid){
        if(StringUtils.isNotBlank(sid)){
            if(Boolean.TRUE.equals(redisTemplate.hasKey(sid + ID_TOKEN_KEY))){
                return this.get(sid + ID_TOKEN_KEY);
            }
        }else{
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object obj =  session.getAttribute(ID_TOKEN_KEY);
                if(obj != null) {
                    return (String)obj;
                }
            }
        }
        return null;
    }

    public void saveRefreshToken(HttpServletRequest request, String sid, String refreshToken){
        if(StringUtils.isNotBlank(sid)){
            this.set(sid + REFRESH_TOKEN_KEY, refreshToken);
            redisTemplate.expire(sid + REFRESH_TOKEN_KEY, expireTime, TimeUnit.MINUTES);
        }else{
            request.getSession().setAttribute(REFRESH_TOKEN_KEY, refreshToken);
        }
    }

    public void updateRefreshToken(HttpServletRequest request, String sid, String refreshToken){
        if(StringUtils.isNotBlank(sid)) {
            if(Boolean.FALSE.equals(redisTemplate.hasKey(sid + REFRESH_TOKEN_KEY))){
                saveRefreshToken(request, sid, refreshToken);
            }else{
                this.set(sid + REFRESH_TOKEN_KEY, refreshToken);
            }
        }else{
            request.getSession().setAttribute(REFRESH_TOKEN_KEY, refreshToken);
        }
    }

    public String getRefreshToken(HttpServletRequest request, String sid){
        if(StringUtils.isNotBlank(sid)){
            if(Boolean.TRUE.equals(redisTemplate.hasKey(sid + REFRESH_TOKEN_KEY))){
                return this.get(sid + REFRESH_TOKEN_KEY);
            }
        }else{
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object obj =  session.getAttribute(REFRESH_TOKEN_KEY);
                if(obj != null) {
                    return (String)obj;
                }
            }
        }
        return null;
    }

    public void saveAccessTokenExpiresAt(HttpServletRequest request, String sid, Instant expiresAt){
        if(StringUtils.isNotBlank(sid)){
            this.set(sid + ACCESS_TOKEN_EXPIRE_AT_KEY, expiresAt.toString());
            redisTemplate.expire(sid + ACCESS_TOKEN_EXPIRE_AT_KEY, expireTime, TimeUnit.MINUTES);
        }else{
            request.getSession().setAttribute(ACCESS_TOKEN_EXPIRE_AT_KEY, expiresAt.toString());
        }
    }

    public void updateAccessTokenExpiresAt(HttpServletRequest request, String sid, Instant expiresAt){
        if(StringUtils.isNotBlank(sid)) {
            if(Boolean.FALSE.equals(redisTemplate.hasKey(sid + ACCESS_TOKEN_EXPIRE_AT_KEY))){
                saveAccessTokenExpiresAt(request, sid, expiresAt);
            }else{
                this.set(sid + ACCESS_TOKEN_EXPIRE_AT_KEY, expiresAt.toString());
            }
        }else{
            request.getSession().setAttribute(ACCESS_TOKEN_EXPIRE_AT_KEY, expiresAt.toString());
        }
    }

    public String getAccessTokenExpiresAt(HttpServletRequest request, String sid){
        if(StringUtils.isNotBlank(sid)){
            if(Boolean.TRUE.equals(redisTemplate.hasKey(sid + ACCESS_TOKEN_EXPIRE_AT_KEY))){
                return this.get(sid + ACCESS_TOKEN_EXPIRE_AT_KEY);
            }
        }else{
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object obj =  session.getAttribute(ACCESS_TOKEN_EXPIRE_AT_KEY);
                if(obj != null) {
                    return (String)obj;
                }
            }
        }
        return null;
    }

    private String get(String key) {
        try {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("get {} error:{}", key, e.getMessage());
            return null;
        }
    }

    private void set(String key, String value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            log.error("set {}={} error:{}", key, value, e.getMessage());
        }
    }
}
